iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
AI Engineering

一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環系列 第 16

Day 16 - Agent 開得越多,我反而越忙:人腦還在當 Scheduler

  • 分享至 

  • xImage
  •  

系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。

我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。

本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。

Today’s Change

  • Base Repo: paulshaclaw

  • Change Ref: PR #112-feat(coordinator): persona-manager headless 自主派工通電(Phase A+B)

  • Issue: 做事、執行與查證被拆給不同 Agent 後,工作已經可以平行處理;但「哪個 Agent 在做哪件事、跑到哪裡、Log 在哪、是不是已經結束」仍然主要靠我自己記。Agent 愈多,我反而愈像值班總機。

  • Root Cause: Interactive Terminal、Agent Process 與 Work Identity 混在一起;tmux Pane 很適合讓人互動,卻只能表示 Agent 暫時坐在哪裡,不能成為一份工作可持續追蹤的 Authority。

  • Solution: 保留 Human Interactive 路徑;Autonomous Dispatch 改走 Headless Executor。派工時帶入 Persona Contract,以 Job Registry 留下 Executor、Session、PID、Log 與 Exit Result,再以跨 Process 可讀的 Exit Sentinel 保存 Completion。

  • Evidence: PR 紀錄全套 1221 passed, 15 skipped;2 個 operator_cockpit 既有 Failure 可在 main 重現,與本 PR 無關。Adversarial Review 抓出 CLI Fanout 未接 Headless,以及 waitpid 無法跨 Process Poll 兩個 High Finding,修正後加入對應回歸。部分真實 Executor Flag/Hook Smoke Test 當時仍列為 Pending,因此本文不把 Unit Test 寫成三家 Agent 已完成完整 Live 驗證。


Agent 終於分工了,然後我開始更忙

上一篇做到最後,群裡已經不能再用一個模糊的 Agent 包辦所有事情。

我開始為Agent取名,給他固定的角色,小bu是builder,專責實作。

小bu看到問題,第一反應永遠是:

「我來。」

然後開始改。

小re則站在旁邊問:

「所以呢?這能證明什麼?」

小re是reviewer,它不幫忙修,只負責找洞、看 Evidence、確認小bu是不是又很有效率地解錯問題。

分工是對的。

工作也真的可以平行。

我會同時開幾個 Terminal:一隻改 Code,一隻跑 Test,一隻讀 Source,一隻 Review 前一隻的結果;另外可能還有 Codex 或 Copilot 在另一個 Worktree 處理別的 Issue。

剛開始很爽。

那種感覺很像修真群裡終於不再只有我一個人在搬磚。大家各自領了任務,閉關的閉關、找碴的找碴,看起來宗門蒸蒸日上。

然後我的 tmux 開始變成記憶力測驗:

Claude    → 這隻在做哪個?
Copilot   → 剛才是不是卡 Permission?
Codex #1  → 它跑完了嗎?
Codex #2  → 這又是哪一份 Worktree?
小re      → 它現在 Review 哪一版?

一隻 Agent 的時候,我只要記一件事:

它現在在做什麼?

五隻一起跑之後,我得記的是:

誰拿了哪個 Task
用哪一份 Context
在哪個 Worktree
目前 Running 還是 Waiting
Log 要去哪裡找
做完後輪到誰

有時它不是做完。

只是卡在 Permission。

有時也不是卡住。

只是正在等我,而我忘了。

Agent 的 Context Window 很大。

但我沒有,人力終有時而窮。

所以表面上的架構是:

Task A → Claude
Task B → Copilot
Task C → Codex #1
Task D → Codex #2
Task E → 小re

實際上比較像:

Claude   ─┐
Copilot  ─┤
Codex #1 ─┼──> 一顆普通工程師的腦袋
Codex #2 ─┤             ↓
小re     ─┘     我是誰?我在哪?下一隻要叫誰?

很好。

五個 Agent 平行工作,最後共用一顆人肉 Scheduler。

我原本想把工作分出去。

結果只是把 Coding 的 Cognitive Load,換成 Monitoring 的 Cognitive Load,再很有禮貌地寄回來給我。

Target


最直覺的做法,當然是把 Pane 管好

既然所有 Agent 都在 tmux 裡,那第一個直覺非常合理:

把 Pane 管好,不就好了?

Manager 派工作時找一個空 Pane,把 Agent 丟進去,再記住:

Task A → Pane %1
Task B → Pane %2
Task C → Pane %3

這至少解決一件事:

我不用再自己記哪一隻 Agent 躲在哪一格。

看起來很好。

老Go看了一眼。

「那 %3 現在是 Running、卡在 Permission,還是其實早就做完了?」

「……我要切過去看。」

「那這份 Task 做完後,Pane 會留著還是重用?」

「應該會重用。」

「所以明天的 %3,跟今天的 %3,可能根本不是同一份工作?」

「對。」

「那你現在記到的是工作,還是工作暫時坐在哪張椅子?」

欸!!!!這盲腸,破了!!!

Pane ID 很適合回答:

我要去哪裡找這隻 Agent?

但 Orchestration 真正需要回答的是:

這份 Work 是誰、交給誰、目前什麼狀態、Log 在哪裡、最後怎麼結束?

PaneAllocator 可以替我管理座位。

我的腦袋還是在管理工作。


Persona Contract 一變長,send-keys 也開始斷章取義

原本 Manager 派工可能只有一句:

fix issue #123

這種東西透過 tmux send-keys 很好用。

但角色拆開後,我不能再只說「幫我修這個」,還得一起帶上 Persona、責任範圍、Plan 與 Task:

Role: reviewer
Responsibility: ...
Constraints: ...
Task: ...
Plan: ...

問題是,Pane Sender 原本只是拿來在 Terminal 打一行字,不是拿來傳一份多行 Contract。

對 Terminal 而言,換行很可能就是 Enter。

所以我以為送進去的是一份完整 Persona Contract,Agent 收到的卻可能是:

Role: reviewer

Enter。

Responsibility: ...

再 Enter。

整份 Prompt 被切成數段,像群裡有人一次只貼一句,還不等前一句看完就連發。

小re看了都不知道自己該先 Review Code,還是先 Review 我的輸入法。

前面才發現 Pane 只能告訴我 Agent 在哪裡,現在連派工本身也開始證明:

Human Interaction 的介面,不一定適合拿來當 Autonomous Dispatch 的介面。

Target


Human 留在 Pane,Autonomous Dispatch 改走 Headless

這次沒有把 tmux 一刀砍掉。

本來有想過,是不是乾脆替 Agent 重做一套全新的平台。

但我跟 Agent 互動時,Pane 其實很好用:

我看輸出
→ 臨時插一句
→ 改方向
→ 再看它回什麼

追求在現存工作流裡讓 Agent 無縫接手,才是男人的浪漫。

所以 Human Interactive 路徑保留。

但 Autonomous Dispatch 改成直接啟動 Agent CLI,也就是 Headless Executor。

先講功能再講名字:

它不是替 Agent 開一格畫面,而是把完整 Prompt 當輸入,啟動一個可以追蹤的 Process。

Claude、Copilot、Codex 的 CLI 參數各有各的脾氣,所以 Manager 不直接到處拼三家的 Command,而是透過 Launcher 把同一份 Work 翻成對應 Executor 的 argv。

這一步看起來只是把 Agent 從前景搬到背景。

真正重要的不是 Background。

是派工終於可以脫離「左邊第二格那隻」這種民間辨識法。

Target


Agent 啟動了,工作還是得有自己的紀錄

Headless 解掉的是 Transport。

但我原本最痛苦的那件事還沒完全消失:

剛才到底派了誰去做什麼?

Headless 啟動 Agent 時,本質上就是啟一個 subprocess。

Process 啟動後,OS 會給它一個 PID;Launcher 再把 PID、Executor、Session 與 Log Path 一起留下來。

每一份派工開始有固定的 Metadata:

executor
session_name
pid
log_path
exit_code

這些資訊被收進 Job Registry

先講功能再講名字:

它就是讓一份工作不再靠「左邊第二個 Pane 那隻」辨認,而是有一筆之後還查得到的紀錄。

以前是:

%1 = Claude = Issue A?
%2 = Copilot = 好像是 Review?
%3 = 剛才那個 Codex 去哪了?

現在至少可以從 Registry 查:

這份 Work
→ 交給哪個 Executor
→ 現在是哪個 Process
→ Log 在哪裡

工作第一次開始脫離「我還記不記得」。

Parallelism 終於沒有再直接跟 Human Memory Capacity 綁死。

但 Registry 裡還有一格很麻煩:

exit_code = ?

Process 到底什麼時候跑完,又要由誰把結果填進來?


waitpid 很誠實:你不是它爸,就別一直問

有了 PID,這題看起來超簡單。

Process 跑完沒?

waitpid 看 Exit Code 不就好了。

第一版真的這樣做。

測試也可以跑。

直到 Adversarial Review 問了一個很關鍵的問題:

「啟動 Agent 的 Process,跟之後 Poll 它的 Process,是同一個嗎?」

不一定。

可能是 Process A 啟動 Agent,自己先結束;過一陣子 Process B 再回來查狀態。

Process A
→ spawn Agent
→ exit

Process B
→ 拿著 PID 回來 Poll
→ 但 Agent 不是它的 Child

waitpid 等的是自己的 Child Process。

知道 PID,不代表你有資格替它收屍。

所以 PID 雖然進了 Registry,Completion 還是綁在啟動它的短命 Process 裡。

這跟我前面的問題其實同一個味道。

以前是:

哪隻 Agent 在做什麼,全部放在我的 Working Memory。

現在變成:

Agent 有沒有跑完,全部放在 Parent Process 才拿得到的狀態。

看起來比較 Software。

本質上沒有比較高尚。

只是換一個地方失憶。

Target


Exit Code 也得留下來,不能陪 Parent Process 一起過世

最後修法其實還滿接地氣的。

Executor 結束時,把 Exit Code 寫成一個獨立檔案;之後不管哪個 Manager Process 回來,都能重新讀取。

概念上就是:

啟動 Executor
→ Executor 結束
→ 寫下 Exit Code

另一個 Process 之後再 Poll:

PID 還活著嗎?
→ 用 Process Liveness 檢查

Exit Result 有了嗎?
→ 讀持久化的 Exit Sentinel

這個小檔案就是 Exit Sentinel

不是什麼很炫的 Distributed Workflow Engine。

它只是在做一件很重要的事:

不要把明天還要知道的結果,只放在今天晚上會死掉的 Process 記憶裡。

同一輪 Review 還抓到另一個 High Finding:Headless 明明已經實作,CLI 的 fanout --executor 卻根本沒接上去。

也就是 API/Test 可以抵達新路徑,使用者真正下 Command 時卻還走不到。

架構圖已經到未來。

入口還住在過去。

修完之後,CLI Fanout 才真的會建立 Headless Launcher,而不是只有測試知道新路徑存在。


群裡開始多了一個只管「這份工作還活著嗎」的傢伙

PR #112 最後紀錄:

1221 passed
15 skipped

另外 2 個 operator_cockpit Failure,在 main 上同樣可以重現,確認不是這次 PR 帶進來的回歸。

新增測試涵蓋 Persona Contract、三家 Executor argv、Job Registry Headless 欄位、跨 Process Completion、Progress Relay,以及 CLI Fanout 到 Headless Launcher 的接線。

Progress Relay 這裡只負責報平安。

Agent Start/Stop 可以回報,但這類 Event 只能說「它有活動」,不能直接升級成「工作正確完成」。

另外,PR 當時也明列部分 Claude/Codex Remote Flag,以及 Copilot/Codex Hook 在真實 Headless 環境仍需要 Smoke Test。

所以 1221 passed 能證明這條控制路徑與契約有被測到。

不能被我翻譯成:

三家 Agent 已經二十四小時自主運轉,穩如老狗。

沒有這個 Evidence。

先不要替它寫績效自評。

不過做到這裡,群裡確實多了一個習慣很奇怪的傢伙。

它不改 Code,也不替小bu辯護,更不跟小re爭論 Finding。

它只會先問:

這是哪一份 Work?
交給誰?
Process 還活著嗎?
Log 在哪裡?
Exit Result 是什麼?

我叫它 小ma

正式角色是 Manager,不是小媽,不過也對,她就是媽媽的角色,管東管西。

它不是因為我們缺一個主管才出現。

剛好相反。

是因為我不想再當所有 Agent 的主管、秘書、總機、簽收人與失物招領處,它才被現實逼出來。

但今天的小ma只知道工作活著還是死了。

它還不知道 Builder 做完後,誰該跑 Gate;Gate 過了,誰留下 Handoff;下一份相依工作,又該由誰叫醒。

老Go看著一筆已經 done 的 Job。

「好,現在你知道它做完了。」

「嗯。」

「然後呢?」

「……」

我的人肉 Scheduler 才剛下掉一半的班。

下一篇,再讓它繼續失業。

Have a nice day.


上一篇
Day 15 - 你說你看過 Source?先把 Tool Call 拿出來 不能再自己證明判準正確
下一篇
Day 17 - Agent 說做完了,Workflow 卻沒往下走:派工之後誰負責收尾?
系列文
一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言